#ifndef SHADER_BUILDER
#define SHADER_BUILDER
#include <iostream>
#include <string.h>
#include <fstream>
#include <sstream>
#include <GL/glew.h>
#include <glm/glm.hpp>


class shaderBuilder{
    unsigned int program;
    std::string vertexShader;
    std::string fragmentShader;
public:
    shaderBuilder();
    shaderBuilder(const shaderBuilder &sb);
    shaderBuilder(const std::string &filepath){

    enum class ShaderType
    {
        NONE = -1, VERTEX = 0, FRAGMENT = 1
    };

    std::ifstream stream(filepath);
    std::string line;
    std::stringstream ss[2];
    ShaderType type = ShaderType::NONE;

    while (getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
                type = ShaderType::VERTEX;
            else if (line.find("fragment") != std::string::npos)
                type = ShaderType::FRAGMENT;
        }
        else
        {
            ss[(int)type] << line << '\n';
        }
    }

    this->vertexShader = ss[0].str();
    this->fragmentShader = ss[1].str();

    this->program = CreateShader(vertexShader, fragmentShader);

    std::cout << "Vertex Shaderi:" << std::endl
              << this->vertexShader << std::endl
              << "Fragment Shader:" << std::endl
              << this->fragmentShader << std::endl;
    }


    static unsigned int CompileShader(unsigned int type, const std::string& source)
    {
        unsigned int id = glCreateShader(type);
        const char* src = source.c_str();
        glShaderSource(id, 1, &src, nullptr);
        glCompileShader(id);

        // Error handling
        int result;
        glGetShaderiv(id, GL_COMPILE_STATUS, &result);
        std::cout << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader compile status: " << result << std::endl;
        if ( result == GL_FALSE )
        {
            int length;
            glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
            char* message = (char*) alloca(length * sizeof(char));
            glGetShaderInfoLog(id, length, &length, message);
            std::cout 
                << "Failed to compile "
                << (type == GL_VERTEX_SHADER ? "vertex" : "fragment")
                << "shader"
                << std::endl;
            std::cout << message << std::endl;
            glDeleteShader(id);
            return 0;
        }

        return id;
    }
    static unsigned int CreateShader(const std::string &vertexShader, const std::string &fragmentShader){
            // create a shader program
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);

    glAttachShader(program, vs);
    glAttachShader(program, fs);

    glLinkProgram(program);

    int program_linked;

    glGetProgramiv(program, GL_LINK_STATUS, &program_linked);
    std::cout << "Program link status: " << program_linked << std::endl;
    if (program_linked != GL_TRUE)
    {
        int log_length = 0;
        char message[1024];
        glGetProgramInfoLog(program, 1024, &log_length, message);
        std::cout << "Failed to link program" << std::endl;
        std::cout << message << std::endl;
    }

    glValidateProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}
   const std::string getVertexShader() const;
   const std::string getFragmentShader() const;
   const unsigned int &getProgram() const;
   void setInt(const std::string &name, int value)const;
   void setMat4(const std::string &matName, glm::mat4 mat);
   void use();
};

#endif
